# Copyright (C) 2014 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import re
import string
import sys
import types


API_DEF = re.compile('^(?:GLES_APIENTRY)\s*\\((.*?)\)',
                     re.MULTILINE | re.DOTALL)


PREAMBLE_TEMPLATE = string.Template("""
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// *************************************************************************
// *************************************************************************
// *************************************************************************
//
//                AUTOGENERATED SOURCE CODE. DO NOT EDIT
//
// Generated with: $GENERATED_WITH
//
// *************************************************************************
// *************************************************************************
// *************************************************************************

""".lstrip())


IMPLEMENTATION_FILE_TEMPLATE = string.Template("""
#include <GLES/gl.h>
#include <GLES/glext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include "common/alog.h"
#include "common/trace_event.h"
#include "graphics_translation/gles/debug.h"
#include "graphics_translation/gles/gles_context.h"

GlesContext* GetCurrentGlesContext();

$IMPLEMENTATION_DECLARATIONS

$PUBLIC_API
""".lstrip())


IMPL_FUNCTION_DECL_TEMPLATE = string.Template("""
${RETURN_TYPE} impl${NAME}(${ARGUMENTS});
""".lstrip())


API_FUNCTION_IMPL_TEMPLATE = string.Template("""
extern "C" ${RETURN_TYPE} gl${NAME}(${ARGUMENTS}) {
  const GlesContext* ctx = GetCurrentGlesContext();
#ifdef ENABLE_API_LOGGING
  ALOGE("[id=%d] ${NAME}(${ARGUMENTS_FORMAT_STRING})",
        ctx ? ctx->GetId() : -1, ${FORMAT_ARGUMENTS});
#endif  // ENABLE_API_LOGGING

#ifdef ENABLE_API_TRACING
  char* args = NULL;
  asprintf(&args, "${ARGUMENTS_FORMAT_STRING}", ${FORMAT_ARGUMENTS});
  TRACE_EVENT2(ARC_TRACE_CATEGORY, "${NAME}",
               "id", ctx ? ctx->GetId() : -1, "args", TRACE_STR_COPY(args));
  free(args);
#endif  // ENABLE_API_TRACING
  return impl${NAME}(${ARGUMENTS_NAME_ONLY});
}

""".lstrip())


def _generate_enum_format(arg_name):
  return '%s (0x%x)', ['GetEnumString(%s)' % arg_name, arg_name]


TYPE_TO_SIMPLE_FORMAT = {
    'EGLImageKHR': '%d',
    'GLbitfield': '0x%x',
    'GLboolean': '%d',
    'GLclampf': '%f',
    'GLclampx': '%d',
    'GLdouble': '%f',
    'GLeglImageOES': '%p',
    'GLenum': _generate_enum_format,
    'GLfixed': '%d',
    'GLfloat': '%f',
    'GLint': '%d',
    'GLintptr': '%ld',  # TODO(crbug.com/441919): Not size portable!
    'GLshort': '%hd',
    'GLsizei': '%zd',
    'GLsizeiptr': '%ld',  # TODO(crbug.com/441919): Not size portable!
    'GLubyte': '%u',
    'GLuint': '%u',
    'const GLchar*': '\\"%s\\"',
    'const GLfloat*': '%p',
    'const GLshort*': '%p',
    'const GLubyte*': '%p',
    'const GLvoid*': '%p',
}


# Specify functions' args that are used in trace macro.
# As to the functions that are not specified in this dict, the first args up to
# two are used.
TRACE_FUNCTION_ARGS = {
    'BufferSubData': [('GLenum', 'target'), ('GLsizeiptr', 'size')],
    'CopyTexImage2D': [('GLsizei', 'width'), ('GLsizei', 'height')],
    'CopyTexSubImage2D': [('GLsizei', 'width'), ('GLsizei', 'height')],
    'MapTexSubImage2DCHROMIUM': [('GLsizei', 'width'), ('GLsizei', 'height')],
    'TexImage2D': [('GLsizei', 'width'), ('GLsizei', 'height')],
    'TexSubImage2D': [('GLsizei', 'width'), ('GLsizei', 'height')],
}


def _generate_args_format_string_and_args(api_name, args):
  format_arguments = []
  format_string = []

  for arg in args:
    arg_type = arg[0]
    arg_names = arg[1:]
    if arg_names:
      arg_format = TYPE_TO_SIMPLE_FORMAT.get(arg_type)
      if arg_format is not None:
        if isinstance(arg_format, (types.FunctionType, types.LambdaType)):
          arg_format, arg_names = arg_format(*arg_names)

        format_string.append(arg_format)
        format_arguments.extend(arg_names)
      elif arg_type.endswith('*'):
        format_string.append('%p')
        format_arguments.extend(arg_names)
      else:
        assert False, 'Unhandled type %s for argument %s for API %s' % (
            arg_type, arg_names, api_name)
  format_string = format_string or ['%s']
  format_arguments = format_arguments or ['""']

  return ', '.join(format_string), format_arguments


def _generate_api_code_with_template(api, template):
  text = []
  for name, ret_type, args in api:
    args_name_only = []
    if len(args) != 1 or args[0][0] != 'void':
      args_name_only = [arg[1] for arg in args]

    args_format_string, format_args = (
        _generate_args_format_string_and_args(name, args))
    format_args = ', '.join(format_args)
    template_dict = {}
    template_dict['NAME'] = name
    template_dict['RETURN_TYPE'] = ret_type
    template_dict['ARGUMENTS'] = ', '.join([' '.join(arg) for arg in args])
    template_dict['ARGUMENTS_NAME_ONLY'] = ','.join(args_name_only)
    template_dict['ARGUMENTS_FORMAT_STRING'] = args_format_string
    template_dict['FORMAT_ARGUMENTS'] = format_args

    text.append(template.substitute(template_dict))

  return ''.join(text).rstrip()


def _generate_impl_declarations(api):
  return _generate_api_code_with_template(api, IMPL_FUNCTION_DECL_TEMPLATE)


def _generate_public_api(api):
  return _generate_api_code_with_template(api, API_FUNCTION_IMPL_TEMPLATE)


def write_implementation(output_file_name, api):
  with open(output_file_name, 'w') as output_file:
    template_dict = {}
    template_dict['GENERATED_WITH'] = ' '.join(sys.argv)
    template_dict['IMPLEMENTATION_DECLARATIONS'] = (
        _generate_impl_declarations(api))
    template_dict['PUBLIC_API'] = _generate_public_api(api)
    output_file.write(PREAMBLE_TEMPLATE.substitute(template_dict))
    output_file.write(IMPLEMENTATION_FILE_TEMPLATE.substitute(template_dict))


def parse_api_entries(input_file_name):
  # For each API function, this function returns a list of tuples of the form:
  #    (api_name, return_type, ((arg1_type, arg1_name), ...))
  api = []
  with open(input_file_name) as input_file:
    for match in API_DEF.finditer(input_file.read()):
      args = match.group(1)
      args = args.split(',')
      args = [arg.strip() for arg in args]
      ret_type = args[0]
      name = args[1]
      args = args[2:]
      args = [arg.rsplit(' ', 1) for arg in args]
      api.append((name, ret_type, args))
  return sorted(api)


def main():
  description = 'Generates the logging stubs from the API'
  parser = argparse.ArgumentParser(description=description)
  parser.add_argument('input', help='Input API file to read')
  parser.add_argument('output_cxx', help='Output .cpp filename')
  args = parser.parse_args()

  api = parse_api_entries(args.input)
  write_implementation(args.output_cxx, api)


if __name__ == '__main__':
  sys.exit(main())
